package cn.chendd.task;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.commons.lang3.reflect.MethodUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.boot.actuate.scheduling.ScheduledTasksEndpoint;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.List;

/**
 * SpringBoot Admin 计划任务增强
 *
 * @author chendd
 * @date 2023/1/28 17:50
 */
@Component
@Aspect
public class TaskDescriptionAop {

    /**
     * 拦截规则：仅ScheduledTasksEndpoint.scheduledTasks方法
     */
    @Pointcut("execution(* org.springframework.boot.actuate.scheduling.ScheduledTasksEndpoint.scheduledTasks())")
    public void pointcutDeclare(){}

    @Around(value = "pointcutDeclare()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        ScheduledTasksEndpoint.ScheduledTasksReport returnValue = (ScheduledTasksEndpoint.ScheduledTasksReport) point.proceed();
        List<ScheduledTasksEndpoint.TaskDescription> cronTasks = returnValue.getCron();
        if (cronTasks == null || cronTasks.isEmpty()) {
            return returnValue;
        }
        //只处理Cron这一种表达式
        int size = cronTasks.size();
        for (int i = 0; i < size; i++) {
            ScheduledTasksEndpoint.TaskDescription cronTask = cronTasks.get(i);
            ScheduledTasksEndpoint.RunnableDescription runnableDescription = cronTask.getRunnable();
            String target = this.getDescription(runnableDescription.getTarget());
            FieldUtils.writeDeclaredField(runnableDescription , "target" , target , true);
        }
        return returnValue;
    }

    /**
     * 重载路径，target格式为：包路径.类名称.方法名称
     * Fixme target的参数值并不是所有的都满足格式，可见源码
     * @since RunnableDescription.RunnableDescription
     * @param target 路径
     * @return 重载后的路径
     */
    public String getDescription(String target) {
        try {
            String className = FilenameUtils.getBaseName(target);
            Class clazz = ClassUtils.getClass(className);
            String methodName = FilenameUtils.getExtension(target);
            Method method = MethodUtils.getAccessibleMethod(clazz , methodName);
            if (method.isAnnotationPresent(TaskDescription.class)) {
                return String.format("%s#%s" , target , method.getAnnotation(TaskDescription.class).value());
            }
        } catch (Exception ignore) {}
        return target;
    }

}
